import { Vector3, CatmullRomCurve3 } from "three";

// 优化：明确 CurveConnection 完整类型（补充 distance）
interface CurveConnection {
  curve1: Vector3;       // 曲线A上的连接点
  curve2: Vector3;       // 曲线B上的连接点
  distance: number;      // 两点间距离（新增）
}

// 优化：RoadObj 接口明确必填属性
export interface RoadObj {
  name: string;          // 曲线名称
  uuid: string;          // 唯一标识
  curve: CatmullRomCurve3; // 曲线对象
}

// 优化：TrackObj 继承 RoadObj 并补充轨迹特有的属性
interface TrackObj extends RoadObj {
  point: Vector3;        // 当前轨迹点坐标
  jzPoint: Vector3;      // 基站坐标
  distanceM: number;     // 距离前一点的米数
  time: number;          // 到达时间
  t: number;             // 曲线上的参数位置
}

// 优化：明确函数参数类型和返回值类型
export function findClosestT (
  curve: CatmullRomCurve3,  // 明确曲线类型
  pointA: Vector3,          // 目标点类型
  precision: number = 1000  // 默认精度
): number {                 // 返回 t 值（0-1）
  let closestT = 0;
  let minDistance = Infinity;

  for (let i = 0; i <= precision; i++) {
    const t = i / precision;
    const samplePoint = curve.getPoint(t);
    const distance = samplePoint.distanceTo(pointA);
    if (distance < minDistance) {
      minDistance = distance;
      closestT = t;
    }
  }
  return closestT;
}

// 优化：补充类型注解和参数校验
export function findTargetT (
  curve: CatmullRomCurve3,  // 明确曲线类型
  tStart: number,           // 起始参数位置（0-1）
  targetDistance: number,   // 目标距离（正数为向前，负数为向后）
  epsilon: number = 0.001   // 精度阈值
): number {
  if (tStart < 0 || tStart > 1) {
    throw new Error(`Invalid tStart value: ${tStart} (must be 0-1)`);
  }

  let tLower: number, tUpper: number;
  if (targetDistance >= 0) {
    tLower = tStart;
    tUpper = 1;
  } else {
    tLower = 0;
    tUpper = tStart;
  }

  let bestT = tStart;
  for (let i = 0; i < 100; i++) {
    const tMid = (tLower + tUpper) / 2;
    const currentLength = getArcLength(curve, tStart, tMid);
    const delta = Math.abs(currentLength) - Math.abs(targetDistance);

    if (Math.abs(delta) < epsilon) {
      bestT = tMid;
      break;
    }

    if (delta < 0) tLower = tMid;
    else tUpper = tMid;
    bestT = tMid;
  }
  return bestT;
}

// 获取曲线长度的函数
export function getArcLength (
  curve: CatmullRomCurve3,  // 明确曲线类型
  tStart: number,           // 起始参数（0-1）
  tEnd: number,             // 结束参数（0-1）
  steps: number = 100       // 采样步数
): number {
  if (tStart < 0 || tStart > 1) throw new Error(`tStart must be 0-1, got ${tStart}`);
  if (tEnd < 0 || tEnd > 1) throw new Error(`tEnd must be 0-1, got ${tEnd}`);
  if (tStart === tEnd) return 0;

  let length = 0;
  const step = (tEnd - tStart) / steps;
  let prevPoint = curve.getPoint(tStart);
  for (let i = 1; i <= steps; i++) {
    const t = Math.max(tStart + step * i, 0);
    const currentPoint = curve.getPoint(t);
    length += prevPoint.distanceTo(currentPoint);
    prevPoint = currentPoint;
  }
  return length;
}

// 优化：使用更严格的类型和可选链校验
export function mergeCurvesSequentially (trackData: TrackObj[]): TrackObj[] {
  if (trackData.length === 0) return [];

  const result: TrackObj[] = [];

  // 处理第一条曲线
  const firstItem = trackData[0];
  if (!firstItem.curve) {
    throw new Error(`${firstItem.name} curve is undefined`);
  }
  const firstT = findClosestT(firstItem.curve, firstItem.jzPoint);
  
  const firstTargetT = findTargetT(firstItem.curve, firstT, firstItem.distanceM);
  const firstPoint = firstItem.curve.getPoint(firstTargetT);

  result.push({
    ...firstItem,
    t: firstTargetT,
    point: firstPoint
  });

  // 处理后续曲线
  for (let i = 1; i < trackData.length; i++) {
    const currentItem = trackData[i];
    if (!currentItem.curve) {
      throw new Error(`${currentItem.name} curve is undefined`);
    }
    const currentT = findClosestT(currentItem.curve, currentItem.jzPoint);
    const currentTargetT = findTargetT(currentItem.curve, currentT, currentItem.distanceM);
    const currentPoint = currentItem.curve.getPoint(currentTargetT);

    // 计算时间差（使用可选链防止日期解析错误）
    const prevTime = new Date(trackData[i - 1].time).getTime();
    const currentTime = new Date(currentItem.time).getTime();
    const duration = prevTime && currentTime
      ? (currentTime - prevTime) / 1000 / 60  // 转换为分钟
      : 0;

    result.push({
      ...currentItem,
      t: currentTargetT,
      point: currentPoint,
      time: duration
    });
  }

  return result;
}

function getCurveConnection (
  curveA1: CatmullRomCurve3,
  curveB1: CatmullRomCurve3,
  threshold: number
): CurveConnection[] {
  const connections: CurveConnection[] = [];
  const curveA = new CatmullRomCurve3().fromJSON(curveA1)
  const curveB = new CatmullRomCurve3().fromJSON(curveB1)

  const len1 = curveA.getLength();
  const len2 = curveB.getLength();

  // 优化：使用 Math.max 确保至少采样10个点
  const samplesA = curveA.getSpacedPoints(Math.max(10, Math.floor(len1 / (threshold / 2))));
  const samplesB = curveB.getSpacedPoints(Math.max(10, Math.floor(len2 / (threshold / 2))));

  for (const p1 of samplesA) {
    for (const p2 of samplesB) {
      const distance = p1.distanceTo(p2);
      if (distance < threshold) {
        connections.push({ curve1: p1, curve2: p2, distance });
        break; // 每对曲线取第一个符合条件的连接点
      }
    }
  }
  return connections;
}

// 生成曲线网络
export function createCurveNetwork (
  curves: RoadObj[],
  threshold: number
): { graph: Map<string, RoadObj[]>; connectionMap: Map<string, CurveConnection[]> } {
  const graph = new Map<string, RoadObj[]>();
  const connectionMap = new Map<string, CurveConnection[]>();

  curves.forEach(curve => {
    graph.set(curve.uuid, []);
  });

  for (let i = 0; i < curves.length; i++) {
    for (let j = i + 1; j < curves.length; j++) {
      const curve1 = curves[i];
      const curve2 = curves[j];
      if (curve1.uuid === curve2.uuid) continue;
      
      const key = [curve1.uuid, curve2.uuid].sort().join('|');
      const connections = getCurveConnection(curve1.curve, curve2.curve, threshold);

      if (connections.length > 0) {
        connectionMap.set(key, connections);
        graph.get(curve1.uuid)?.push(curve2);
        graph.get(curve2.uuid)?.push(curve1);
      }
    }
  }

  return { graph, connectionMap };
}

// 优化：A* 算法类型增强
function findCurvePath (
  graph: Map<string, TrackObj[]>,
  start: TrackObj,
  end: TrackObj,
  connectionMap: Map<string, CurveConnection[]>
): Vector3[][] | null {
  // 明确优先队列元素类型
  type OpenSetItem = {
    path: TrackObj[];
    g: number;
    h: number;
    f: number;
    con: CurveConnection[];
  };

  const openSet: OpenSetItem[] = [];
  const visited = new Map<string, number>();

  // 优化：启发式函数使用连接点距离或曲线最近点距离
  const heuristic = (current: TrackObj): number => {
    if (current.uuid === end.uuid) return 0;

    const key = [current.uuid, end.uuid].sort().join('|');
    const connections = connectionMap.get(key) || [];

    if (connections.length > 0) {
      return connections.reduce((min, conn) => Math.min(min, conn.distance), Infinity);
    }
    // 备用方案：取曲线离散点到终点的最小距离
    return current.curve.getSpacedPoints(100)
      .reduce((min, point) => Math.min(min, point.distanceTo(end.point)), Infinity);
  };

  // 初始化起点（明确类型断言）
  const startH = heuristic(start);
  openSet.push({
    path: [start],
    g: 0,
    h: startH,
    f: startH,
    con: []
  });
  visited.set(start.uuid, 0);

  while (openSet.length > 0) {
    // 按 f 值升序排列
    openSet.sort((a, b) => a.f - b.f);
    const current = openSet.shift()!;
    const currentNode = current.path[current.path.length - 1];

    // 到达终点处理
    if (currentNode.uuid === end.uuid) {
      const pathPoints: Vector3[][] = [];
      for (let i = 0; i < current.path.length; i++) {
        const node = current.path[i]
        const startT = i === 0
          ? findClosestT(node.curve, node.point)
          : findClosestT(node.curve, current.con[i - 1].curve2);

        const endT = i === current.path.length - 1
          ? findClosestT(node.curve, end.point)
          : findClosestT(node.curve, current.con[i].curve1);

        pathPoints.push(extractSubPoints(node.curve, startT, endT));
      }
      return pathPoints;
    }

    // 扩展邻居节点
    const neighbors = graph.get(currentNode.uuid) || [];
    for (const neighbor of neighbors) {
      const key = [currentNode.uuid, neighbor.uuid].sort().join('|');
      const connections = connectionMap.get(key) || [];
      if (connections.length === 0) continue;

      if (neighbor.curve?.metadata) {
        neighbor.curve = new CatmullRomCurve3().fromJSON(neighbor.curve)
      }

      // 寻找最小代价连接点
      const minConn = connections.reduce((min, conn) =>
        conn.distance < min.distance ? conn : min, connections[0]);

      const newG = current.g + minConn.distance;
      const newH = heuristic(neighbor);
      const newF = newG + newH;
      const newPath = [...current.path, neighbor];
      const newCon = [...current.con, minConn];

      // 检查是否找到更优路径
      const existingG = visited.get(neighbor.uuid);
      if (!existingG || newG < existingG) {
        visited.set(neighbor.uuid, newG);
        openSet.push({ path: newPath, g: newG, h: newH, f: newF, con: newCon });
      }
    }
  }

  return null;
}

// 优化：明确参数类型和返回值类型
function extractSubPoints (
  originalCurve: CatmullRomCurve3,
  startT: number,
  endT: number,
  segments: number = 100
): Vector3[] {
  if (startT < 0 || startT > 1) throw new Error(`startT must be 0-1, got ${startT}`);
  if (endT < 0 || endT > 1) throw new Error(`endT must be 0-1, got ${endT}`);

  const points: Vector3[] = [];
  for (let i = 0; i <= segments; i++) {
    const t = startT + (endT - startT) * (i / segments);
    points.push(originalCurve.getPoint(t));
  }
  return points;
}

// 优化：明确参数类型和返回值类型
export function findTrackPath (
  trackPoints: TrackObj[],
  graph: Map<string, TrackObj[]>,
  connectionMap: Map<string, CurveConnection[]>
): { path: Vector3[]; time: number }[] {
  const pathResult: { path: Vector3[]; time: number }[] = [];

  for (let i = 0; i < trackPoints.length - 1; i++) {
    const start = trackPoints[i];
    const end = trackPoints[i + 1];

    // 同一曲线处理（使用可选链和类型断言）
    if (start.uuid === end.uuid && start.curve) {
      const subPath = extractSubPoints(start.curve, start.t, end.t);
      pathResult.push({ path: subPath, time: end.time });
      continue;
    }

    // 不同曲线处理
    const curvePath = findCurvePath(graph, start, end, connectionMap);
    if (!curvePath) throw new Error(`No path found between ${start.name} and ${end.name}`);

    pathResult.push({
      path: curvePath.flat(),
      time: end.time
    });
  }

  return pathResult;
}